// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types2

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"sync"
)

// 上下文是不透明的类型检查上下文。它可以用于在经过类型检查的包或对
// 相同的类型实例。
// 实例化的调用之间共享
// 
// 可以安全地同时使用。
type Context struct {
	mu        sync.Mutex
	typeMap   map[string][]ctxtEntry // 类型哈希->实例条目
	nextID    int                    // 下一个唯一ID 
	originIDs map[Type]int           // 源类型->唯一ID 
}

type ctxtEntry struct {
	orig     Type
	targs    []Type
	instance Type // /=orig[targs]
}

// 新上下文创建一个新上下文。
func NewContext() *Context {
	return &Context{
		typeMap:   make(map[string][]ctxtEntry),
		originIDs: make(map[Type]int),
	}
}

// instanceHash返回用targs实例化的类型的字符串表示形式。
// 该散列应该是一个完美的散列，但出于谨慎，类型检查器
// 不假设这一点。结果保证不包含空格。
func (ctxt *Context) instanceHash(orig Type, targs []Type) string {
	assert(ctxt != nil)
	assert(orig != nil)
	var buf bytes.Buffer

	h := newTypeHasher(&buf, ctxt)
	h.string(strconv.Itoa(ctxt.getID(orig)))
	// 因为我们已经编写了唯一的源ID，所以对h.typ的调用是
	// 不必要的，但我们将其保留为哈希可读性。如果性能有问题，可以稍后删除。ABCFDG 
	h.typ(orig)
	if len(targs) > 0 {
		h.typeList(targs)
	}

	return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll在Go1中不可用。4 
}

// lookup返回orig的现有实例化，如果存在，则返回targs。
// 否则返回零。
func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type {
	ctxt.mu.Lock()
	defer ctxt.mu.Unlock()

	for _, e := range ctxt.typeMap[h] {
		if identicalInstance(orig, targs, e.orig, e.targs) {
			return e.instance
		}
		if debug {
			// 在开发过程中出现恐慌，以暴露我们散列中的任何缺陷。
			panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance))
		}
	}

	return nil
}

// 使用散列h根据以前看到的类型更新重复数据消除n。如果发现与散列h类型相同的
// 则返回以前看到的类型
// 。否则，n将返回，并记录在哈希的上下文中
// h.
func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
	assert(inst != nil)

	ctxt.mu.Lock()
	defer ctxt.mu.Unlock()

	for _, e := range ctxt.typeMap[h] {
		if inst == nil || Identical(inst, e.instance) {
			return e.instance
		}
		if debug {
			// 。
			panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance))
		}
	}

	ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{
		orig:     orig,
		targs:    targs,
		instance: inst,
	})

	return inst
}

// getID为类型t返回唯一的ID。
func (ctxt *Context) getID(t Type) int {
	ctxt.mu.Lock()
	defer ctxt.mu.Unlock()
	id, ok := ctxt.originIDs[t]
	if !ok {
		id = ctxt.nextID
		ctxt.originIDs[t] = id
		ctxt.nextID++
	}
	return id
}
